/*
 * Copyright 2012 Marsrutai.lt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package lt.marsrutai.mobile.android.model.enums;

import java.io.Serializable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class DateTime implements Serializable {

    private static final long serialVersionUID = -7719380832256477102L;

    private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getTimeZone("EET");
    private static final String JSON_TIME_TEMPLATE = "/Date(%s+0200)/";
    private static final String FORMAT_STRING_FULL = "yyyy-MM-dd'T'HH:mm";
    private static final String FORMAT_STRING_HOUR_MINUTE = "HH:mm";
    private static final String FORMAT_STRING_HOUR_MINUTE_SECOND = "HH:mm:ss";

    private Calendar calendar;

    /**
     * Gets current time, assuming your timezone is EET, even if it's not.
     * NOTE: this is much slower than DateTime(millis) - use it only if you really need current time
     */
    public DateTime() {
        // If current time is 15:00 (ANY timezone), we want to return 15:00 (EET timezone). So:
        // 1) Take current time 15:00 (ANY)
        // 2) Serialize it as yyyy-MM-hhT15:00 using formatFull
        // 3) Deserialize it as 15:00 (EET) using parseFromFull
        calendar = DateTime.parseFromFull(DateTime.formatFull(Calendar.getInstance())).getCalendar();
    }

    public DateTime(String jsonFormat) {
        initFromMillis(parseMillisFromJSON(jsonFormat));
    }

    public DateTime(long mills) {
        initFromMillis(mills);
    }

    private void initFromMillis(long millis) {
        calendar = Calendar.getInstance(DEFAULT_TIMEZONE);
        calendar.setTime(new Date(millis));
    }



    public long getMills() {
        return calendar.getTimeInMillis();
    }

    /**
     * SUNDAY - 0 MONDAY - 1 ... SATURDAY - 6
     *
     * @return
     */
    public int getWeekDay() {
        return calendar.get(Calendar.DAY_OF_WEEK) - 1;
    }

    public int getCurrentYear() {
        return calendar.get(Calendar.YEAR);
    }

    public int getCurrentMonth() {
        return calendar.get(Calendar.MONTH);
    }

    public int getCurrentDay() {
        return calendar.get(Calendar.DAY_OF_MONTH);
    }

    public int getCurrentHour() {
        return calendar.get(Calendar.HOUR_OF_DAY);
    }

    public int getCurrentMinute() {
        return calendar.get(Calendar.MINUTE);
    }

    public Calendar getCalendar() {
        return calendar;
    }

    public int minutesUntil(Calendar that) {
        long remainingMinutes = Math.round((that.getTimeInMillis() - this.getMills())/60000.0);
        return (int)remainingMinutes;
    }

    public int compareTimeTo(DateTime that) {
        final int BEFORE = -1;
        final int EQUAL = 0;
        final int AFTER = 1;

        if (this == that) {
            return EQUAL;
        }

        if (getCalendar().get(Calendar.HOUR_OF_DAY) < that.getCalendar().get(Calendar.HOUR_OF_DAY)) {
            return BEFORE;
        }
        else if (getCalendar().get(Calendar.HOUR_OF_DAY) > that.getCalendar().get(Calendar.HOUR_OF_DAY)) {
            return AFTER;
        }
        else if (getCalendar().get(Calendar.MINUTE) < that.getCalendar().get(Calendar.MINUTE)) {
            return BEFORE;
        }
        else if (getCalendar().get(Calendar.MINUTE) > that.getCalendar().get(Calendar.MINUTE)) {
            return AFTER;
        }
        else {
            return EQUAL;
        }
    }


    public static Long parseMillisFromJSON(String datetime) {
        Long milliseconds = Long.parseLong(datetime.substring(6, datetime.indexOf("+")));
        return milliseconds;
    }


    /**
     *
     * @param datetime
     *            Datetime in output format. For example: 2012-10-27T04:07
     *
     * @return
     */
    public static DateTime parseFromFull(String datetime) {

        try {
            SimpleDateFormat format = new SimpleDateFormat(FORMAT_STRING_FULL, Locale.US);
            format.setTimeZone(DEFAULT_TIMEZONE);
            Date date = format.parse(datetime);
            return new DateTime(date.getTime());
        }
        catch (ParseException e) {
            e.printStackTrace();
            return new DateTime(); // Do we really want to swallow this exception? It was like this before refactoring (Jurgis 20130107)
        }
    }

    public static String format(Calendar calendar, String formatString) {
        SimpleDateFormat format = new SimpleDateFormat(formatString, Locale.US);
        format.setTimeZone(calendar.getTimeZone());
        return format.format(calendar.getTime());
    }

    public String format(String formatString) {
        return format(this.getCalendar(), formatString);
    }

    public static String formatFull(Calendar calendar) {
        return format(calendar, FORMAT_STRING_FULL);
    }

    public String formatFull() {
        return formatFull(this.getCalendar());
    }

    public static String formatHourMinute(Calendar calendar) {
        return format(calendar, FORMAT_STRING_HOUR_MINUTE);
    }

    public static String formatHourMinuteRounded(Calendar calendar) {
        if (calendar.get(Calendar.SECOND) >= 30) calendar.add(Calendar.MINUTE, 1);
        String result = format(calendar, FORMAT_STRING_HOUR_MINUTE);
        if (calendar.get(Calendar.SECOND) >= 30) calendar.add(Calendar.MINUTE, -1); // Restore value
        return result;
    }

    public static String formatHourMinuteSecond(Calendar calendar) {
        return format(calendar, FORMAT_STRING_HOUR_MINUTE_SECOND);
    }

    public String formatHourMinute() {
        return formatHourMinute(this.getCalendar());
    }

    public static String formatJSON(Calendar calendar) {
        return String.format(JSON_TIME_TEMPLATE, calendar.getTimeInMillis());
    }
}
